#include "tcptunnel.h"
#include <QTimerEvent>
#include <QDateTime>
#include <QSslSocket>
#include <QSslConfiguration>
#include <QSslKey>
#include <QSslError>
#include <QFileInfo>
#include <QCoreApplication>
#include "pcapio.h"
tcpTunnel::tcpTunnel(QObject * parent)
	:QObject(parent)
	,m_svr( new SSLServer(this))
{
	connect(m_svr,&SSLServer::sig_newClient,this,&tcpTunnel::slot_new_connection);
}

void  tcpTunnel::timerEvent(QTimerEvent * e)
{
	// 获取定时器ID
	int tid = e->timerId();
	// 如果定时器ID等于m_nEmitter
	if (tid == m_nEmitter)
	{
		// 如果m_sock存在且状态为ConnectedState
		if (m_sock && m_sock->state()==QTcpSocket::ConnectedState)
		{
			// 获取PCAPIO接收位置
			quint64 rp = PCAPIO::pcap_recv_pos;
			// 如果m_nTPos加上PCAPIO_BUFCNT小于rp
			if (m_nTPos + PCAPIO_BUFCNT < rp)
			{
				// 发射sig_message信号，提示TCP隧道溢出，清除
				emit sig_message(tr("TCP tunnel overflow, clear."));
				// 将m_nTPos设置为rp-1
				m_nTPos = rp-1;
			}
			// 当m_nTPos小于rp时
			while (m_nTPos < rp) {
				// 获取当前位置
				const int nPOS = m_nTPos % PCAPIO_BUFCNT;
				// 获取from_id
				const int fromID = PCAPIO::global_buffer[nPOS].from_id;
				// 如果fromID不等于TCPTUNID，且len小于65536，且len大于0
				if (fromID !=TCPTUNID
						&& PCAPIO::global_buffer[nPOS].len < 65536
						&& PCAPIO::global_buffer[nPOS].len >0)
				{
					// 定义hd数组
					const unsigned char  hd[] = {0x18u,0x24u,0x7eu,0x69u};
					// 获取len
					const unsigned short len = PCAPIO::global_buffer[nPOS].len;
					// 将hd数组写入m_sock
					m_sock->write((const char *)hd,4);
					// 将len写入m_sock
					m_sock->write((const char *)&len,2);
					// 将data写入m_sock
					m_sock->write(PCAPIO::global_buffer[nPOS].data.constData(),len);
				}
				// m_nTPos自增
				++m_nTPos;
			}
		}
	}
	// 如果定时器ID等于m_nTimer
	else if (tid==m_nTimer)
	{
		// 如果m_bListen为false
		if (!m_bListen)
		{
			// 如果m_sock存在
			if (m_sock)
			{
				// 如果m_lastState不等于m_sock的状态
				if (m_lastState!=m_sock->state())
				{
					// 发射sig_message信号，提示Socket状态变化
					emit sig_message(tr("Socket State: %1->%2").arg(m_lastState).arg(m_sock->state()));
					// 将m_lastState设置为m_sock的状态
					m_lastState = m_sock->state();
				}
				// 如果m_sock的状态不是ConnectedState，ConnectingState，HostLookupState
				if(m_sock->state()!=QTcpSocket::ConnectedState &&
						m_sock->state()!=QTcpSocket::ConnectingState &&
						m_sock->state()!=QTcpSocket::HostLookupState
						)
				{
					// 中止m_sock
					m_sock->abort();
					// 将m_sock转换为QSslSocket指针
					QSslSocket * sslsock = qobject_cast<QSslSocket*>(m_sock);
					// 如果sslsock存在
					if (sslsock)
						// 连接到加密的主机
						sslsock->connectToHostEncrypted(m_str_addr,m_n_port);
					// 否则连接到主机
					else
						m_sock->connectToHost(QHostAddress(m_str_addr),m_n_port);
				}

			}
		}
		// 否则
		else
		{
			// 如果m_svr没有监听
			if (!m_svr->isListening())
			{
				// 如果m_svr监听成功
				if (m_svr->listen(QHostAddress(m_str_addr),m_n_port))
				{
					// 发射sig_message信号，提示监听成功
					emit sig_message(tr("Listening on port %1").arg(m_n_port));
				}
				// 否则
				else
				{
					// 发射sig_message信号，提示无法打开端口
					emit sig_message(tr("Can not open port %1").arg(m_n_port));
				}

			}
			// 如果m_sock存在
			if (m_sock)
			{
				// 如果m_lastState不等于m_sock的状态
				if (m_lastState!=m_sock->state())
				{
					// 发射sig_message信号，提示Socket状态变化
					emit sig_message(tr("Socket State: %1->%2").arg(m_lastState).arg(m_sock->state()));
					// 将m_lastState设置为m_sock的状态
					m_lastState = m_sock->state();
				}
			}

		}
	}
	// 调用QObject的timerEvent函数
	return QObject::timerEvent(e);
}

void tcpTunnel::slot_new_connection(qintptr socketDescriptor)
{
	// 如果当前有连接，则断开连接，并发出消息通知
	if (m_sock)
	{
		disconnect(m_sock,&QTcpSocket::readyRead,this,&tcpTunnel::slot_read_sock);
		emit sig_message(tr("Old Client %1:%2 has been kicked off .").arg(m_sock->peerAddress().toString()).arg(m_sock->peerPort()));
		m_sock->abort();
		m_sock->deleteLater();
		m_sock = nullptr;
	}
	// 创建新的QTcpSocket或QSslSocket对象
	QTcpSocket * sock = m_bSSL? new QSslSocket(this):new QTcpSocket(this);
	// 如果设置socket描述符成功，则连接信号槽，并发出消息通知
	if (sock->setSocketDescriptor(socketDescriptor)) {
		connect(sock,&QTcpSocket::readyRead,this,&tcpTunnel::slot_read_sock);
		emit sig_message(tr("New Client Arrival At %1:%2  .").arg(sock->peerAddress().toString()).arg(sock->peerPort()));

		//SSL Handshake
		QSslSocket * sslsock = qobject_cast<QSslSocket*>(sock);
		if (sslsock)
		{
			// 连接加密信号槽，发出消息通知
			connect(sslsock,&QSslSocket::encrypted,[this]()->void{emit sig_message(tr("Encrypted."));});
			// 连接SSL错误信号槽，发出消息通知
			connect(sslsock,
					static_cast<void (QSslSocket::*)(const QList <QSslError> &)>(&QSslSocket::sslErrors),
					[this](const QList <QSslError> & err)->void{
				foreach (QSslError e, err)
					emit sig_message(tr("SSL Error %1:%2 .").arg((int)e.error()).arg(e.errorString()));
			});
			// 设置证书和私钥路径
			QString strCerPath =  ":/certs/svr_cert.pem";
			QString strPkPath =  ":/certs/svr_privkey.pem";
			QString externalCert = QCoreApplication::applicationDirPath()+"/svr_cert.pem";
			QString externalPk = QCoreApplication::applicationDirPath()+"/svr_privkey.pem";
			if (QFileInfo::exists(externalCert) && QFileInfo::exists(externalPk))
			{
				strCerPath = externalCert;
				strPkPath = externalPk;
			}
			// 加载证书和私钥
			sslsock->setLocalCertificate(strCerPath);
			emit sig_message(tr("SSL Server Key %1 loaded .").arg(strCerPath));

			sslsock->setPrivateKey(strPkPath);
			emit sig_message(tr("SSL Private Key %1 loaded .").arg(strPkPath));

			// 开始SSL加密
			sslsock->startServerEncryption();
		}
		// 将新的socket对象赋值给m_sock
		m_sock = sock;
	}
	else
	{
		// 如果设置socket描述符失败，则删除socket对象
		delete sock;
	}
}

void tcpTunnel::startWork(QString address, QString port, bool listen,bool ssl)
{
	// 设置地址、端口、监听状态和SSL状态
	m_str_addr = address;
	m_n_port = port.toInt();
	m_bListen = listen;
	m_bSSL = ssl;
	// 如果定时器已经启动，则停止工作
	if (m_nTimer>=0)
	{
		stopWork();
	}
	// 如果定时器未启动，则开始工作
	if (m_nTimer<0)
	{
		// 如果是监听状态
		if (m_bListen)
		{
			// 如果服务器正在监听，则关闭服务器
			if (m_svr->isListening())
				m_svr->close();
			// 开始监听指定地址和端口
			if (true==m_svr->listen(QHostAddress(m_str_addr),m_n_port))
			{
				// 发送消息，表示监听成功
				emit sig_message(tr("Listening on port %1").arg(m_n_port));
			}
			else
			{
				// 发送消息，表示监听失败
				emit sig_message(tr("Can not open port %1").arg(m_n_port));
			}
		}
		// 如果不是监听状态
		else
		{
			// 如果客户端已经连接，则断开连接
			if (m_sock)
			{
				// 发送消息，表示客户端已经断开连接
				emit sig_message(tr("Client %1:%2 has been turnned off .").arg(m_sock->peerAddress().toString()).arg(m_sock->peerPort()));
				m_sock->abort();
				m_sock->deleteLater();
				m_sock = nullptr;
			}
			// 如果是SSL连接
			if (m_bSSL)
			{
				// 获取默认的SSL配置
				QSslConfiguration cfg = QSslConfiguration::defaultConfiguration();
				// 获取外部证书路径
				QString externalCert = QCoreApplication::applicationDirPath()+"/ca_cert.pem";
				// 如果外部证书存在，则加载外部证书
				if (cfg.addCaCertificates(externalCert))
					emit sig_message(tr("Load CA Cert from %1.").arg(externalCert));
				else
				{
					// 如果外部证书不存在，则加载默认的证书
					cfg.addCaCertificates(":/certs/ca_cert.pem");
					emit sig_message(tr("CA Cert not found :%1. Default demo ca_cert loaded.").arg(externalCert));
				}
				// 设置默认的SSL配置
				QSslConfiguration::setDefaultConfiguration(cfg);

				// 创建SSL套接字
				QSslSocket * sslsock = new QSslSocket(this);
				// 当SSL连接建立时，发送消息
				connect(sslsock,&QSslSocket::encrypted,[this]()->void{emit sig_message(tr("Encrypted."));});
				// 当SSL连接出现错误时，忽略错误
				connect(sslsock,
						static_cast<void (QSslSocket::*)(const QList <QSslError> &)>(&QSslSocket::sslErrors),
						[this,sslsock](const QList <QSslError> & err)->void{
					QList<QSslError> errIgnore;
					foreach (QSslError e, err)
					{
						emit sig_message(tr("SSL Error %1:%2 .").arg((int)e.error()).arg(e.errorString()));
						if (e.error()==QSslError::UnableToGetIssuerCertificate) errIgnore<<e;
						else if (e.error()==QSslError::HostNameMismatch) errIgnore<<e;
						else if (e.error()==QSslError::CertificateUntrusted) errIgnore<<e;
						else if (e.error()==QSslError::CertificateNotYetValid) errIgnore<<e;
						else if (e.error()==QSslError::CertificateExpired) errIgnore<<e;
					}
					sslsock->ignoreSslErrors(errIgnore);
				});
				// 将SSL套接字赋值给m_sock
				m_sock = sslsock;
				// 连接到指定的地址和端口
				sslsock->connectToHostEncrypted(m_str_addr,m_n_port);
			}
			// 如果不是SSL连接
			else
			{
				// 创建TCP套接字
				m_sock = new QTcpSocket(this);
				// 连接到指定的地址和端口
				m_sock->connectToHost(QHostAddress(m_str_addr),m_n_port);
			}
			// 当套接字有数据可读时，调用slot_read_sock函数
			connect(m_sock,&QTcpSocket::readyRead,this,&tcpTunnel::slot_read_sock);
			// 发送消息，表示连接到指定的地址和端口
			emit sig_message(tr("connecting to: %1:%2").arg(m_str_addr).arg(m_n_port));
		}
		// 启动定时器，每秒触发一次
		m_nTimer=startTimer(1000);
		// 启动定时器，每10毫秒触发一次
		m_nEmitter = startTimer(10);
		// 获取当前PCAP接收位置
		m_nTPos = PCAPIO::pcap_recv_pos;
		// 初始化捕获包数量
		m_total_caps = 0;
	}

}

void tcpTunnel::stopWork()
{
	if (m_nTimer>=0)
	{
		killTimer(m_nEmitter);
		m_nEmitter = -1;
		killTimer(m_nTimer);
		m_nTimer = -1;
		if (m_svr->isListening())
			m_svr->close();
		if (m_sock)
		{
			emit sig_message(tr("Client %1:%2 has been turnned off .").arg(m_sock->peerAddress().toString()).arg(m_sock->peerPort()));
			m_sock->abort();
			m_sock->deleteLater();
			m_sock = nullptr;
		}
		m_lastState = QTcpSocket::UnconnectedState;
		m_total_caps = 0;
	}
}


void tcpTunnel::slot_read_sock()
{
	//读取所有数据
	QByteArray arrData = m_sock->readAll();
	//将数据添加到数组中
	m_package_array.append(arrData);
	//0x18,0x24,0x7E,0x69 + LENGH(2) + DATA
	//当数组中数据长度大于等于6时，开始处理数据
	while (static_cast<size_t>(m_package_array.size())>=6)
	{
		//检查Magic
		int goodoff = 0;
		//循环检查Magic
		while (!(m_package_array[0+goodoff]==(char)0x18 && m_package_array[1+goodoff]==(char)0x24
				 &&m_package_array[2+goodoff]==(char)0x7E  &&m_package_array[3+goodoff]==(char)0x69 ))
		{
			//如果Magic不匹配，则将数组中的数据向前移动
			++goodoff;
			//如果数组中数据长度小于等于3，则跳出循环
			if (goodoff+3>= m_package_array.size())
				break;
		}
		//如果goodoff不为0，则将数组中的数据向前移动
		if (goodoff)
			m_package_array.remove(0,goodoff);
		//如果数组中数据长度小于6，则跳出循环
		if (static_cast<size_t>(m_package_array.size())< 6 )
			break;
		//获取数据长度
		const unsigned short  * ptrlen = (const unsigned short *)
				(m_package_array.constData() + 4);

		const unsigned short datalen = *ptrlen;

		//如果数组中数据长度小于datalen+6，则跳出循环
		if (static_cast<size_t>(m_package_array.size())<datalen+6)
			break;
		//获取数据指针
		const char * dptr = m_package_array.constData()+6;
		//将数据加入队列
		if (PCAPIO::enqueuePack(dptr,datalen,TCPTUNID,TCPPORTNAME))
		{
			//每1000次发送一次信号
			if (++m_total_caps % 1000==0)
				emit sig_message(tr("TCP recv %1").arg(m_total_caps));
		}
		//将数组中的数据向前移动
		m_package_array.remove(0,6+datalen);
	}
}

